spb/earth-now Public License
earth-now.co — real-time planetary dashboard: live world metrics modeled, not streamed.
TypeScript 93%
Shell 2.3%
SQL 1.4%
JavaScript 1.3%
Dockerfile 1.2%
CSS 0.8%
1/**2 * earth-now.co3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: apps/web/app/metric/[id]/page.tsx6 * Purpose: Public per-metric page — live counter, real-time model chart and the metric's own methodology7 */89import type { Metadata } from "next";10import { notFound } from "next/navigation";11import MetricDetail from "@/components/MetricDetail";12import { fetchMetrics, fetchModels } from "@/lib/api";1314export const dynamic = "force-dynamic";1516interface Params {17 params: { id: string };18}1920export async function generateMetadata({ params }: Params): Promise<Metadata> {21 try {22 const metrics = await fetchMetrics();23 const metric = metrics.find((m) => m.id === params.id);24 if (metric === undefined) return { title: "earth-now.co" };25 return {26 title: `${metric.name.fr} — earth-now.co`,27 description: `${metric.name.fr} / ${metric.name.en} — compteur live piloté par un modèle statistique documenté (${metric.model}).`,28 };29 } catch {30 return { title: "earth-now.co" };31 }32}3334export default async function MetricPage({ params }: Params) {35 let metric;36 let model = null;37 try {38 const [metrics, models] = await Promise.all([fetchMetrics(), fetchModels()]);39 metric = metrics.find((m) => m.id === params.id);40 model = models.models[params.id] ?? null;41 } catch {42 // API down: 404 rather than a broken page (the dashboard has the offline state).43 notFound();44 }45 if (metric === undefined) notFound();46 return <MetricDetail metric={metric} model={model} />;47}48